package gamesys.games.particlesystem.core
{
	import gamesys.games.particlesystem.effects.IEffect;
	import gamesys.games.particlesystem.emitters.IEmitter;
	import gamesys.games.particlesystem.graphics.BitmapAnimationFactory;
	import gamesys.games.particlesystem.graphics.BitmapCanvas;
	import gamesys.games.particlesystem.utils.ArrayUtils;
	import gamesys.games.particlesystem.utils.MathUtils;
	import gamesys.games.particlesystem.utils.Range;

	import flash.display.BitmapData;
	import flash.display.Sprite;

	/**
	 * @author robertthurston
	 */
	public class ParticleSystem extends Sprite
	{
		private var _reSpawn : Boolean;
		private var _startDelay : Range;
		private var _particlesGraphics : Array;
		private var _emitter : IEmitter;
		private var _canvas : BitmapCanvas;
		private var _particleDataArray : Array;
		private var _effects : Array;
		private var _totalParticles : uint;
		private var _alphaBitmapDataArray : Array;

		public function ParticleSystem(canvas : BitmapCanvas, emitter : IEmitter, particleGraphics : Array)
		{
			_canvas = canvas;
			_emitter = emitter;
			_particlesGraphics = BitmapAnimationFactory.getInstance().getMultipleBitmapAnimations(particleGraphics);

			setupDefaultSettings();
			createAlphasThisNeedsMovingAsTempLocation();
		}

		private function setupDefaultSettings() : void
		{
			_particleDataArray = new Array();
			_effects = new Array();
			_startDelay = new Range(0, 0);
			_totalParticles = 0;
		}

		public function set startDelay(startDelay : Range) : void
		{
			_startDelay = startDelay;
		}

		public function set reSpawn(reSpawn : Boolean) : void
		{
			_reSpawn = reSpawn;
		}

		public function addEffect(effect : IEffect) : uint
		{
			_effects.push(effect);

			return _effects.length - 1;
		}

		private function createAlphasThisNeedsMovingAsTempLocation() : void
		{
			_alphaBitmapDataArray = new Array();

			var colours : Array = [0x11FFFFFF, 0x22FFFFFF, 0x33FFFFFF, 0x55FFFFFF, 0x66FFFFFF, 0x88FFFFFF, 0x99FFFFFF, 0xBBFFFFFF, 0xDDFFFFFF, 0xEEFFFFFF];

			for (var i : int = 0;i < colours.length;i++)
			{
				var bitmapData : BitmapData = new BitmapData(25, 25, true, colours[i]);
				_alphaBitmapDataArray.push(bitmapData);
			}
		}

		public function addParticles(quantity : int) : void
		{
			for (var i : int = 0;i < quantity;i++)
			{
				var particleData : ParticleData = new ParticleData(_alphaBitmapDataArray);

				resetParticleSettings(particleData);
				_particleDataArray.push(particleData);
			}

			_totalParticles = _particleDataArray.length;
		}

		private function resetParticleSettings(particleData : ParticleData) : void
		{
			var length : int = _effects.length;

			particleData.x = _emitter.startX;
			particleData.y = _emitter.startY;
			particleData.reSpawn = _reSpawn;
			particleData.startDelay = _startDelay.range;
			particleData.bitmapAnimation = _particlesGraphics[MathUtils.randomUINT(_particlesGraphics.length)];

			for (var i : int = 0;i < length;i++)
			{
				IEffect(_effects[i]).reset(particleData);
			}
		}

		public function update() : void
		{
			var needsCompacting : Boolean;

			for (var i : int = 0;i < _totalParticles;i++)
			{
				var particleData : ParticleData = _particleDataArray[i];

				if (particleData.startDelay > 0)
				{
					particleData.startDelay--;
				}
				else if (particleData.life > 1)
				{
					updateAndDraw(particleData);
				}
				else if (particleData.reSpawn)
				{
					resetParticleSettings(particleData);
				}
				else
				{
					needsCompacting = true;
					_particleDataArray[i] = null;
				}
			}

			if (needsCompacting)
			{
				ArrayUtils.compact(_particleDataArray);
				_totalParticles = _particleDataArray.length;
			}
		}

		private function updateAndDraw(particleData : ParticleData) : void
		{
			var length : int = _effects.length;

			particleData.px = particleData.x;
			particleData.py = particleData.y;

			for (var i : int = 0;i < length;i++)
			{
				IEffect(_effects[i]).update(particleData);
			}

			_canvas.copyPixels(particleData.nextFrame, particleData.x, particleData.y, particleData.alphaBitmapData);
		}

		public function removeEffect(effectID : uint) : void
		{
			_effects[effectID] = null;
			ArrayUtils.compact(_effects);
		}

		public function removeAllEffects() : void
		{
			_effects = [];
		}

		public function get totalParticles() : uint
		{
			return _totalParticles;
		}

		public function stopEmitting() : void
		{
			for (var i : int = 0;i < _totalParticles;i++)
			{
				var particleData : ParticleData = _particleDataArray[i];

				particleData.reSpawn = false;

				if (particleData.startDelay > 0)
				{
					_particleDataArray[i] = null;
				}
			}

			ArrayUtils.compact(_particleDataArray);

			_totalParticles = _particleDataArray.length;
		}

		public function reset() : void
		{
			_particleDataArray = new Array();
			_totalParticles = _particleDataArray.length;
		}
	}
}
